/**
 * @file Star.cpp
 *
 * Reprezentacja gwiazdy zmiennej - implementacja.
 *
 * @author Zbigniew Siciarz
 * @date 2009
 * @version 2.0.0
 * @since 2.0.0
 */

#include "Star.h"

#include <QMapIterator>
#include <QRegExp>


namespace Dogz
{
    QMap<QString, QString> Star::constellations;

    QMap<QString, QString> Star::greekLetters;

    /**
     * Tworzy obiekt gwiazdy.
     *
     * Inicjalizuje wszystkie dane pustymi wartościami, a następnie próbuje
     * odgadnąć, czy podano nazwę, desygnację, czy AUID gwiazdy.
     *
     * @param name nazwa, desygnacja lub AUID gwiazdy
     */
    Star::Star(const QString& name):
        m_name(""), m_designation(""), m_auid(""), m_type(""),
        m_maxMagnitude(0.0), m_minMagnitude(0.0), m_period(0.0),
        m_epoch(0.0)
    {
        guessName(name);
    }

    /**
     * Ustawia zakres zmian jasności.
     *
     * @param maxMagnitude jasność w maksimum - liczbowo mniejsza
     * @param minMagnitude jasność w minimum - liczbowo większa
     */
    void Star::setMagnitudeRange(float maxMagnitude, float minMagnitude)
    {
        m_minMagnitude = minMagnitude;
        m_maxMagnitude = maxMagnitude;
    }

    /**
     * Sprawdza, czy gwiazda jest okresowa.
     *
     * @return true jeśli gwiazda ma niezerowy okres
     */
    bool Star::isPeriodical() const
    {
        // nie można != 0.0, ze względu na ograniczenia dokładności typu
        return m_period > 0.00000001;
    }

    /**
     * Zwraca fazę gwiazdy, bądź wartość ujemną dla nieokresowych.
     *
     * @param jd JD dla którego liczymy fazę
     * @return faza w danym JD
     */
    double Star::getPhase(double jd) const
    {
        if (!isPeriodical())
            return -1.0;

        double periodsFromEpoch = (jd - m_epoch) / m_period;

        // zwracamy część ułamkową
        return periodsFromEpoch - static_cast<int>(periodsFromEpoch);
    }


    /**
     * Próbuje odgadnąć format nazwy - czy to może jest desygnacja
     * harvardzka albo AUID. A jeśli nie, to po prostu przypisuje nazwę.
     *
     * @param name prawdopodobna nazwa gwiazdy.
     */
    void Star::guessName(const QString& name)
    {
        static QString auidPattern = "^\\d{3}-[a-zA-Z]{3}-\\d{3}$";
        static QString harvardPattern = "^\\d{4}(\\+|-){1}\\d{2}\\w*$";

        if (QRegExp(auidPattern).exactMatch(name))
        {
            m_auid = name;
        }
        else if (QRegExp(harvardPattern).exactMatch(name))
        {
            m_designation = name;
        }
        else
        {
            m_name = name;
        }
    }

    /**
     * Normalizuje nazwę gwiazdy.
     *
     * Jeśli nazwa jest postaci tradycyjnej, tzn. oznaczenie i gwiazdozbiór,
     * to zamienia oznaczenie na duże litery i normalizuje skrót gwiazdozbioru.
     * W przeciwnym wypadku nie modyfikuje nazwy.
     *
     * @param name nazwa gwiazdy
     * @return znormalizowana nazwa
     */
    QString Star::normalizeStarName(const QString& name)
    {
        static QRegExp re("^([a-zA-Z0-9]+) ([a-zA-Z]{3})");
        if (re.exactMatch(name))
        {
            QString star = re.cap(1).toUpper();
            QString constellation = re.cap(2);

            // mapowanie nazw gwiazdozbiorów
            if (constellations.isEmpty())
                initConstellations();

            return star + " " + constellations[constellation.toUpper()];
        }
        else
        {
            return name;
        }
    }

    /**
     * Normalizuje grecką literę w nazwie gwiazdy.
     *
     * @param name nazwa gwiazdy
     * @return znormalizowana gwiazda
     * @todo: zoptymalizować!
     */
    QString Star::fixGreekLetter(const QString& name)
    {
        if (greekLetters.empty())
            initGreekLetters();

        QString newName = name;
        QMapIterator<QString, QString> i(greekLetters);
        QRegExp regexp;
        regexp.setCaseSensitivity(Qt::CaseInsensitive);
        while (i.hasNext())
        {
            i.next();
            regexp.setPattern(i.key());
            newName = newName.replace(regexp, i.value());
        }

        return newName;
    }

    /**
     * Zapełnia odwzorowanie skrótów gwiazdozbiorów na poprawną wielkość liter.
     *
     * Wygenerowane automatycznie za pomocą skryptu w Pythonie.
     */
    void Star::initConstellations()
    {
        constellations["AND"] = "And";
        constellations["ANT"] = "Ant";
        constellations["APS"] = "Aps";
        constellations["AQR"] = "Aqr";
        constellations["AQL"] = "Aql";
        constellations["ARA"] = "Ara";
        constellations["ARI"] = "Ari";
        constellations["AUR"] = "Aur";
        constellations["BOO"] = "Boo";
        constellations["CAE"] = "Cae";
        constellations["CAM"] = "Cam";
        constellations["CNC"] = "Cnc";
        constellations["CVN"] = "CVn";
        constellations["CMA"] = "CMa";
        constellations["CMI"] = "CMi";
        constellations["CAP"] = "Cap";
        constellations["CAR"] = "Car";
        constellations["CAS"] = "Cas";
        constellations["CEN"] = "Cen";
        constellations["CEP"] = "Cep";
        constellations["CET"] = "Cet";
        constellations["CHA"] = "Cha";
        constellations["CIR"] = "Cir";
        constellations["COL"] = "Col";
        constellations["COM"] = "Com";
        constellations["CRA"] = "CrA";
        constellations["CRB"] = "CrB";
        constellations["CRV"] = "Crv";
        constellations["CRT"] = "Crt";
        constellations["CRU"] = "Cru";
        constellations["CYG"] = "Cyg";
        constellations["DEL"] = "Del";
        constellations["DOR"] = "Dor";
        constellations["DRA"] = "Dra";
        constellations["EQU"] = "Equ";
        constellations["ERI"] = "Eri";
        constellations["FOR"] = "For";
        constellations["GEM"] = "Gem";
        constellations["GRU"] = "Gru";
        constellations["HER"] = "Her";
        constellations["HOR"] = "Hor";
        constellations["HYA"] = "Hya";
        constellations["HYI"] = "Hyi";
        constellations["IND"] = "Ind";
        constellations["LAC"] = "Lac";
        constellations["LEO"] = "Leo";
        constellations["LMI"] = "LMi";
        constellations["LEP"] = "Lep";
        constellations["LIB"] = "Lib";
        constellations["LUP"] = "Lup";
        constellations["LYN"] = "Lyn";
        constellations["LYR"] = "Lyr";
        constellations["MEN"] = "Men";
        constellations["MIC"] = "Mic";
        constellations["MON"] = "Mon";
        constellations["MUS"] = "Mus";
        constellations["NOR"] = "Nor";
        constellations["OCT"] = "Oct";
        constellations["OPH"] = "Oph";
        constellations["ORI"] = "Ori";
        constellations["PAV"] = "Pav";
        constellations["PEG"] = "Peg";
        constellations["PER"] = "Per";
        constellations["PHE"] = "Phe";
        constellations["PIC"] = "Pic";
        constellations["PSA"] = "PsA";
        constellations["PSC"] = "Psc";
        constellations["PUP"] = "Pup";
        constellations["PYX"] = "Pyx";
        constellations["RET"] = "Ret";
        constellations["SGE"] = "Sge";
        constellations["SGR"] = "Sgr";
        constellations["SCO"] = "Sco";
        constellations["SCL"] = "Scl";
        constellations["SCT"] = "Sct";
        constellations["SER"] = "Ser";
        constellations["SEX"] = "Sex";
        constellations["TAU"] = "Tau";
        constellations["TEL"] = "Tel";
        constellations["TRA"] = "TrA";
        constellations["TRI"] = "Tri";
        constellations["TUC"] = "Tuc";
        constellations["UMA"] = "UMa";
        constellations["UMI"] = "UMi";
        constellations["VEL"] = "Vel";
        constellations["VIR"] = "Vir";
        constellations["VOL"] = "Vol";
        constellations["VUL"] = "Vul";
    }

    void Star::initGreekLetters()
    {
        greekLetters["^(alp|alf|alpha|alfa)\\b"] = "alpha";
        greekLetters["^(bet|beta)\\b"] = "beta";
        greekLetters["^(gam|gamma)\\b"] = "gamma";
        greekLetters["^(del|delta)\\b"] = "delta";
        greekLetters["^(eps|epsilon)\\b"] = "epsilon";
        greekLetters["^(zet|zeta)\\b"] = "zeta";
        greekLetters["^(eta)\\b"] = "eta";
        greekLetters["^(the|tet|theta|teta)\\b"] = "theta";
        greekLetters["^(iot|iota)\\b"] = "iota";
        greekLetters["^(kap|kappa)\\b"] = "kappa";
        greekLetters["^(lam|lambda)\\b"] = "lambda";
        greekLetters["^(mu|mi)\\b"] = "mu";
        greekLetters["^(nu|ni)\\b"] = "nu";
        greekLetters["^(ksi|xi)\\b"] = "xi";
        greekLetters["^(omi|omicron)\\b"] = "omicron";
        greekLetters["^(pi)\\b"] = "pi";
        greekLetters["^(ro|rho)\\b"] = "rho";
        greekLetters["^(sig|sigma)\\b"] = "sigma";
        greekLetters["^(tau)\\b"] = "tau";
        greekLetters["^(ups|upsilon|yps|ypsilon)\\b"] = "upsilon";
        greekLetters["^(fi|phi)\\b"] = "phi";
        greekLetters["^(chi)\\b"] = "chi";
        greekLetters["^(psi)\\b"] = "psi";
        greekLetters["^(ome|omega)\\b"] = "omega";
    }
}
